﻿using System.Runtime.InteropServices;
using System.Threading.Channels;
using Serilog;
using ZCommon;
using ZDriver;

namespace Model;

public class ProtocolParser
{
    // 业务数据缓存
    private readonly Dictionary<int, List<byte>> _totalRawDataBuffer = new();
    private readonly object _totalRawDataBufferLock = new();

    // Channel读取线程运行标志,用于退出线程
    private readonly CancellationTokenSource _cts = new();

    // 外部输入的数据通道<串口接收的原始数据>
    private readonly Channel<byte[]> _originalDataChn = Channel.CreateUnbounded<byte[]>();

    // 给业务数据处理的通道<节点ID>
    private readonly Channel<int> _rawDataChan = Channel.CreateUnbounded<int>();
    
    // 短响应事件<状态码>
    public event Action<int>? DevUpdateShortReplyEvent;
    
    // 长响应事件<状态码>
    public event Action<int>? DevUpdateLongReplyEvent;
    
    // 一帧数据响应事件<状态码>
    public event Action<UpdateOneFrameResponse>? DevUpdateFrameReplyEvent;

    public ProtocolParser()
    {
        // 启动接收任务
        _ = TryGetRawDataAsync();
        // 启动帧处理任务
        _ = TryGetFrameAsync();
        // 连接数据接收
        Singleton<SerialPort_Driver>.Instance().DataReceived += OnDataReceived;
    }

    // 事件定义
    // 新的一帧数据到达<节点ID,一帧数据>
    public event Action<int, List<byte>>? OnOneFrame;

    // 心跳帧到达
    public event Action? OnHeart;

    // 登录事件<节点ID>
    public event Action<int>? OnLogin;

    // 登出事件<节点ID>
    public event Action<int>? OnLogout;

    // 更新当前登录节点列表<当前登录的节点ID列表>
    public event Action<List<int>>? OnUpdateLoginNode;

    private void OnDataReceived(object? sender, byte[] data)
    {
        // 写入通道
        _originalDataChn.Writer.WriteAsync(data);
    }

    // 接收数据,写入通道
    private async Task TryGetRawDataAsync()
    {
        // 数据缓存
        var rawFrameData = new FrameData();

        while (!_cts.IsCancellationRequested)
        {
            // 收到数据,ConfigureAwait显示后台处理,不切换回主线程
            var tempData = await _originalDataChn.Reader.ReadAsync(_cts.Token).ConfigureAwait(false);
            rawFrameData.TotalMsg.AddRange(tempData);

            var needFind = true;
            while (needFind)
                switch (rawFrameData.CurStatus)
                {
                    case GetFrameStatus.FindHeader:
                        needFind = TryGetRawHeader(ref rawFrameData);
                        break;
                    case GetFrameStatus.FindType:
                        needFind = TryGetRawType(ref rawFrameData);
                        break;
                    case GetFrameStatus.FindLength:
                        needFind = TryGetRawLength(ref rawFrameData);
                        break;
                    case GetFrameStatus.FindBody:
                        needFind = TryGetRawBody(ref rawFrameData);
                        break;
                    case GetFrameStatus.Checksum:
                        needFind = TryCheckRawSum(ref rawFrameData);
                        break;
                }
        }

        /* 带超时的读取示例
        var readTask = _dataChn.Reader.ReadAsync(_cts.Token).AsTask();
        var delayTask = Task.Delay(200, _cts.Token);

        var completedTask = await Task.WhenAny(readTask, delayTask);
        if (completedTask == readTask)
        {
            return await readTask; // 成功读取数据
        }
        else
        {
            return null; // 超时
        }
        */
    }

    private bool TryGetRawHeader(ref FrameData data)
    {
        // 没有足够数据则返回
        if (data.TotalMsg.Count < Marshal.SizeOf<ZPS_Pc>()) return false;

        // 帧头
        var header = new List<byte> {0x95, 0x27};
        // 找帧头
        var headerIdx = data.TotalMsg.IndexOf(header);
        if (headerIdx == -1)
        {
            // 没有数据,清空
            data.TotalMsg = [];
            return false;
        }

        // 移除前面的无效数据
        data.TotalMsg.RemoveRange(0, headerIdx);

        // 重新找头
        if (headerIdx > 0) return true;

        // 获得整个头
        data.OneFrameHeader =
            CommonFunc.ByteArrayToStructure<ZPS_Pc>(data.TotalMsg.GetRange(0, Marshal.SizeOf<ZPS_Pc>()));

        // 跳过前面的EB 90
        data.TotalMsg.RemoveRange(0, header.Count);

        // 计算帧头校验
        var headerCalSum = ProtocolStructCommon.Checksum_ZPS_PC(data.OneFrameHeader);
        if (headerCalSum != data.OneFrameHeader.U16HeaderChecksum)
        {
            Log.Error("ZPS_Pc header checksum error");
            return true;
        }

        // 寻找类型
        data.CurStatus = GetFrameStatus.FindType;

        return true;
    }

    private bool TryGetRawType(ref FrameData data)
    {
        // type没有什么好解的,已经存在oneFrameHeader中
        data.CurStatus = GetFrameStatus.FindLength;
        return true;
    }

    private bool TryGetRawLength(ref FrameData data)
    {
        // 查看当前的帧长是否正确
        if (data.OneFrameHeader.U32Length > ProtocolStructCommon.ZPS_PcDataMaxLength)
        {
            Log.Error($"frame length error,{data.OneFrameHeader.U32Length}>{ProtocolStructCommon.ZPS_PcDataMaxLength}");
            data.CurStatus = GetFrameStatus.FindHeader;
        }
        else
        {
            // 合理的帧长,寻找数据
            data.CurStatus = GetFrameStatus.FindBody;
        }

        return true;
    }

    private bool TryGetRawBody(ref FrameData data)
    {
        // 查看数据是否足够,头长度-2(EB 90已经跳过)+帧长
        if (data.TotalMsg.Count < Marshal.SizeOf<ZPS_Pc>() - 2 + data.OneFrameHeader.U32Length)
            // 数据不够
            return false;

        // 数据够了,储存当前的数据
        data.FrameBody = data.TotalMsg.GetRange(Marshal.SizeOf<ZPS_Pc>() - 2, (int) data.OneFrameHeader.U32Length)
            .ToArray();

        // 数据存储完成,进行校验
        data.CurStatus = GetFrameStatus.Checksum;
        return true;
    }

    private bool TryCheckRawSum(ref FrameData data)
    {
        // 计算数据校验
        var dataCalSum = ProtocolStructCommon.Checksum_Pure(data.FrameBody);

        // 对比
        if (dataCalSum != data.OneFrameHeader.U32DataChecksum)
        {
            Log.Error("ZPS_Pc data checksum error");
            data.CurStatus = GetFrameStatus.FindHeader;
            return true;
        }

        // 跳过数据
        data.TotalMsg.RemoveRange(0, (int) (Marshal.SizeOf<ZPS_Pc>() - 2 + data.OneFrameHeader.U32Length));

        // 数据都正确
        // 区分数据
        switch ((PSPCType) data.OneFrameHeader.U8Type)
        {
            // 0x00 心跳帧,定时发送
            case PSPCType.Heart_Type:
                OnHeart?.Invoke();
                break;

            // 0x10 登录
            case PSPCType.Login_Type:
                OnLogin?.Invoke(data.OneFrameHeader.U16Id);
                break;

            // 0x11 离线
            case PSPCType.Logout_Type:
                OnLogout?.Invoke(data.OneFrameHeader.U16Id);
                break;

            // 0x12 当前登录的节点
            case PSPCType.CurNodesReply_Type:
                var dataSize = data.OneFrameHeader.U32Length;
                var nodes = new List<int>();

                for (var i = 0; i < dataSize; i += 2)
                {
                    // 每次读取 2 字节并转换为 UInt16，然后转为 int
                    var value = BitConverter.ToUInt16(data.FrameBody, i);
                    nodes.Add(value);
                }

                OnUpdateLoginNode?.Invoke(nodes);
                break;

            // 0x20 业务数据
            case PSPCType.Data_Type:
                lock (_totalRawDataBufferLock)
                {
                    if (!_totalRawDataBuffer.ContainsKey(data.OneFrameHeader.U16Id))
                        _totalRawDataBuffer[data.OneFrameHeader.U16Id] = [];

                    _totalRawDataBuffer[data.OneFrameHeader.U16Id].AddRange(data.FrameBody);
                }

                // 通知数据到了
                _rawDataChan.Writer.WriteAsync(data.OneFrameHeader.U16Id);
                break;

            /* 升级数据 */
            // 升级状态
            case PSPCType.UpdateDevStatus_Type:
                var tempRawStatus = CommonFunc.ByteArrayToStructure<UpdateStatus>(data.FrameBody);
                // 当前状态
                var tempStatus = (UpdateStatusEnum)tempRawStatus.U8Status;
                switch (tempStatus)
                {
                    case UpdateStatusEnum.StartUpdateDev_Status:
                    case UpdateStatusEnum.InvalidNodeId_Status:
                        DevUpdateShortReplyEvent?.Invoke((int) tempStatus);
                        break;

                    case UpdateStatusEnum.ReceiveDone_Status:
                    case UpdateStatusEnum.CheckDone_Status:
                    case UpdateStatusEnum.EraseFlashDone_Status:
                    case UpdateStatusEnum.WriteFlashDone_Status:
                    case UpdateStatusEnum.ReceiveError_Status:
                    case UpdateStatusEnum.CheckError_Status:
                    case UpdateStatusEnum.FlashError_Status:
                    case UpdateStatusEnum.Unknown_Status:
                        DevUpdateLongReplyEvent?.Invoke((int) tempStatus);
                        break;
                }

                break;

            // 一帧数据响应
            case PSPCType.UpdateDevFrameReply_Type:
                var tempFrameReply = CommonFunc.ByteArrayToStructure<UpdateOneFrameResponse>(data.FrameBody);
                DevUpdateFrameReplyEvent?.Invoke(tempFrameReply);
                break;

            default:
                Log.Error("Unknown ZPS_Pc type");
                break;
        }

        data.CurStatus = GetFrameStatus.FindHeader;
        return true;
    }

    private async Task TryGetFrameAsync()
    {
        while (!_cts.IsCancellationRequested)
        {
            // 收到数据,ConfigureAwait显示后台处理,不切换回主线程
            var nodeId = await _rawDataChan.Reader.ReadAsync(_cts.Token).ConfigureAwait(false);

            lock (_totalRawDataBufferLock)
            {
                if (!_totalRawDataBuffer.TryGetValue(nodeId, out var nodeBuffer)) continue;

                // 寻找有几个帧头C0
                var headerCount = nodeBuffer.Count(b => b == 0xC0);

                // 一个头都没有,数据都是无效的,清除掉
                if (headerCount == 0)
                {
                    nodeBuffer.Clear();
                }
                else
                {
                    // 查看第一个头的位置
                    var findResultIdx = nodeBuffer.IndexOf(0xC0);
                    // 将头前面无效的数据移除
                    nodeBuffer.RemoveRange(0, findResultIdx);

                    // 查看此时的header数,至少有2个则认为可能是1帧数据,取出帧数据
                    if (headerCount > 1)
                    {
                        // 取出2个头之间的数据,验证是否为1帧合格的数据
                        while (true)
                        {
                            // 数据太少,直接返回
                            if (nodeBuffer.Count < 2) break;

                            // 此时开头的必定是C0,直接找第二个C0
                            var findNextHeaderIdx = nodeBuffer.IndexOf(0xC0, 1);

                            // 这个应该不存在,因为headerCount>1
                            if (findNextHeaderIdx == -1)
                                // 没有下一个C0了
                                break;

                            // 获得此时的帧,至少是C0开头,C0结尾,需要进行译码
                            var tempRawFrame0 = nodeBuffer.Take(findNextHeaderIdx + 1).ToArray();

                            // 验证帧是否合格,合格则从buffer中移除,记录到待返回的数据中,如果不合格则只留下尾部C0,前面的抛掉
                            // 如果帧数据太少(<7)则直接不合格,无需解析
                            if (tempRawFrame0.Length < 7)
                            {
                                // 保留最后的C0
                                nodeBuffer.RemoveRange(0, findNextHeaderIdx);
                            }
                            else
                            {
                                // 初始化译码后的帧,原始帧数据
                                var tempFrame = new List<byte>();

                                // 译码
                                for (var i = 0; i < tempRawFrame0.Length; ++i)
                                    // 最后1个字符是C0,至少倒数第三个字符才有译码的可能
                                    if (tempRawFrame0[i] == 0xDB && i < tempRawFrame0.Length - 2)
                                    {
                                        // 记录译码值,并往后移动1位
                                        if (tempRawFrame0[i + 1] == 0xDC)
                                        {
                                            // DBDC->C0
                                            tempFrame.Add(0xC0);
                                            ++i;
                                        }
                                        else if (tempRawFrame0[i + 1] == 0xDD)
                                        {
                                            // DBDD->DB
                                            tempFrame.Add(0xDB);
                                            ++i;
                                        }
                                        else
                                        {
                                            // 记录常规值
                                            tempFrame.Add(tempRawFrame0[i]);
                                        }
                                    }
                                    else
                                    {
                                        // 记录常规值
                                        tempFrame.Add(tempRawFrame0[i]);
                                    }

                                // 验证数据长度是否合格
                                // 获得长度
                                var frameLength = (tempFrame[1] << 8) + tempFrame[2];

                                // 不含: 开始标志,信息长度字段自身,校验和,结束标志
                                if (frameLength != tempFrame.Count - 5)
                                {
                                    // 长度异常,无效帧,保留最后的C0
                                    nodeBuffer.RemoveRange(0, tempRawFrame0.Length - 1);

                                    continue;
                                }

                                // 长度合格,crc校验
                                var crcData = tempFrame.Skip(1).Take(tempFrame.Count - 3).ToArray();
                                var crc = ProtocolStructCommon.CheckXOR(crcData);

                                if (crc == tempFrame[^2])
                                {
                                    // 校验正确
                                    // 发送正确的帧数据
                                    OnOneFrame?.Invoke(nodeId, tempFrame);

                                    // 从buffer中移除数据
                                    nodeBuffer.RemoveRange(0, tempRawFrame0.Length);

                                    // 找到下一个开头,保持buffer以C0开头
                                    var findResultAgain = nodeBuffer.IndexOf(0xC0);

                                    if (findResultAgain > 0)
                                        // 将头前面无效的数据移除
                                        nodeBuffer.RemoveRange(0, findResultAgain);
                                    else if (findResultAgain == -1) nodeBuffer.Clear();
                                }
                                else
                                {
                                    Log.Warning("crc error");
                                    // 校验错误,无效帧,保留最后的C0
                                    nodeBuffer.RemoveRange(0, tempRawFrame0.Length - 1);
                                }
                            }
                        }
                    }
                    else
                    {
                        // 此时只有1个头,查看此时数据量,如果太大则认为都是无效数据,抛掉
                        if (nodeBuffer.Count > 10240) nodeBuffer.Clear();
                    }
                }
            }
        }
    }

    // 获得帧的状态机状态
    private enum GetFrameStatus
    {
        // 找帧头
        FindHeader,

        // 找类型
        FindType,

        // 找帧长
        FindLength,

        // 找数据体
        FindBody,

        // 检查校验和
        Checksum
    }

    // 帧数据容器
    private struct FrameData
    {
        // 总数据容器
        public List<byte> TotalMsg = [];

        // 一帧数据的头
        public ZPS_Pc OneFrameHeader = new();

        // 一帧的数据内容(一帧中除帧头、帧ID、帧长度、校验、帧尾)
        public byte[] FrameBody = [];

        // 当前帧状态
        public GetFrameStatus CurStatus = GetFrameStatus.FindHeader;

        public FrameData()
        {
        }
    }
}